use std::cell::{Cell, RefCell};
use std::collections::HashMap;
use std::hash::Hash;
use std::sync::{Arc, Barrier, Condvar, Mutex, Once};
use std::thread;
use std::thread::LocalKey;
use std::time::Duration;

use rand::{thread_rng, Rng};
use thread_local::ThreadLocal;

use crate::gen_title;

pub fn ch4_12_1() {
    gen_title("ch4_12_1", 18);

    let handle = thread::spawn(|| {
        for i in 1..10 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }

    handle.join().unwrap();
}

pub fn ch4_12_2() {
    gen_title("ch4_12_2", 18);

    let handle = thread::spawn(|| {
        for i in 1..5 {
            println!("hi number {} from the spawned thread!", i);
            thread::sleep(Duration::from_millis(1));
        }
    });

    handle.join().unwrap();

    for i in 1..5 {
        println!("hi number {} from the main thread!", i);
        thread::sleep(Duration::from_millis(1));
    }
}

pub fn ch4_12_3() {
    gen_title("ch4_12_3", 18);

    let v = vec![1, 2, 3];

    let handle = thread::spawn(move || {
        println!("Here's a vector: {:?}", v);
    });

    handle.join().unwrap();
}

pub fn ch4_12_4() {
    gen_title("ch4_12_4", 18);

    let new_thread = thread::spawn(move || {
        thread::spawn(move || {
            println!("I am a new thread");
        })
    });

    new_thread.join().unwrap();
    println!("child thread is finish!!");

    thread::sleep(Duration::from_millis(100));
}

pub fn ch4_12_5() {
    gen_title("ch4_12_5", 18);

    let mut handles = Vec::with_capacity(6);
    let barrier = Arc::new(Barrier::new(6));

    for _ in 0..6 {
        let b = barrier.clone();
        handles.push(thread::spawn(move || {
            println!("thread {:?} is waiting", thread::current().id());
            b.wait();
            println!("thread {:?} is running", thread::current().id());
        }))
    }

    for handle in handles {
        handle.join().unwrap();
    }
}

pub fn ch4_12_6() {
    gen_title("ch4_12_6", 18);

    thread_local!(static FOO:RefCell<u32> = RefCell::new(1));

    FOO.with(|f| {
        assert_eq!(*f.borrow(), 1);
        *f.borrow_mut() = 2;
        println!("thread {:?} is running", thread::current().id());
        println!("f: {:?}", *f.borrow());
    });

    let t = thread::spawn(move || {
        FOO.with(|f| {
            assert_eq!(*f.borrow(), 1);
            *f.borrow_mut() = 3;
            println!("thread {:?} is running", thread::current().id());
            println!("f: {:?}", *f.borrow());
        });
    });

    t.join().unwrap();

    FOO.with(|f| {
        assert_eq!(*f.borrow(), 2);
        println!("thread {:?} is running", thread::current().id());
        println!("f: {:?}", *f.borrow());
    });
}

pub fn ch4_12_7() {
    gen_title("ch4_12_7", 18);

    struct Foo;
    impl Foo {
        thread_local! {
            static FOO:RefCell<usize>=RefCell::new(0);
        }
    }

    Foo::FOO.with(|x| println!("{}", x.borrow()));
}

pub fn ch4_12_8() {
    gen_title("ch4_12_8", 18);

    thread_local! {
        static FOO:RefCell<usize> = RefCell::new(0);
    }

    struct Bar {
        foo: &'static LocalKey<RefCell<usize>>,
    }

    impl Bar {
        fn constructor() -> Self {
            Self { foo: &FOO }
        }
    }

    Bar::constructor().foo.with(|x| println!("{}", x.borrow()));
}

pub fn ch4_12_9() {
    gen_title("ch4_12_9", 18);

    let tls = Arc::new(ThreadLocal::new());
    let mut v = vec![];

    for _ in 0..5 {
        let tls2 = tls.clone();
        let handle = thread::spawn(move || {
            let cell = tls2.get_or(|| Cell::new(0));
            cell.set(cell.get() + 1);
        });

        v.push(handle);
    }

    for handle in v {
        handle.join().unwrap();
    }

    let tls = Arc::try_unwrap(tls).unwrap();
    let total = tls.into_iter().fold(0, |x, y| {
        println!("x: {}, y: {}", x, y.get());
        x + y.get()
    });

    assert_eq!(total, 5);
    println!("total: {}", total);
}

pub fn ch4_12_10() {
    gen_title("ch4_12_10", 18);

    let pair = Arc::new((Mutex::new(false), Condvar::new()));
    let pair2 = pair.clone();

    thread::spawn(move || {
        let (lock, cvar) = &*pair2;
        let mut started = lock.lock().unwrap();
        println!("changing started");
        *started = true;
        cvar.notify_one();
    });

    let (lock, cvar) = &*pair;
    let mut started = lock.lock().unwrap();
    while !*started {
        started = cvar.wait(started).unwrap();
    }

    println!("started changed");
}

pub fn ch4_12_11() {
    gen_title("ch4_12_11", 18);

    static mut VAL: usize = 0;
    static INIT: Once = Once::new();

    let handle1 = thread::spawn(move || {
        INIT.call_once(|| unsafe {
            VAL = 1;
        });
    });

    let handle2 = thread::spawn(move || {
        INIT.call_once(|| unsafe {
            VAL = 2;
        });
    });

    handle1.join().unwrap();
    handle2.join().unwrap();

    println!("VAL: {}", unsafe { VAL });
}
